#ifndef DATA
#define DATA

#include <iostream>
#include <fstream>
#include <string>
#include "fileUtil.hpp"
#include "config.hpp"
#include <vector>
#include <unordered_map>
#include "jsonUtil.hpp"

#define SERVERPATH "/server/"

namespace bucai
{
    typedef struct DataInfo //管理已经上传的文件信息
    {
        bool pack_flag;
        time_t mtime;
        time_t atime;
        size_t fsize;
        std::string realpath;
        std::string url;
        std::string packpath;

        bool NewDataInfo(const std::string &realpath)
        {
            bucai::fileUtil fu(realpath);
            bucai::Config *con = bucai::Config::getConfig();
            pack_flag = false;
            mtime = fu.LastMTime();
            atime = fu.LastATime();
            fsize = fu.FileSize();
            this->realpath = realpath;
            // /download/ + name
            url = con->GetURLPrefix() + fu.FileName();
            // packdir + name + .zip
            packpath = con->GetPackDir() + fu.FileName() + con->GetArcSuffix();
        }
    } DataInfo;

    class DataManager
    {
    private:
        std::string _manager_file;
        pthread_rwlock_t _rwlock;
        std::unordered_map<std::string, DataInfo> _table;

    public:
        DataManager()
        {
            _manager_file = bucai::Config::getConfig()->GetManagerFile();
            pthread_rwlock_init(&_rwlock, NULL);
        }
        ~DataManager()
        {
            pthread_rwlock_destroy(&_rwlock);
        }
        bool InitLoad()
        { // 初始化程序运⾏时从⽂件读取数据
            bucai::fileUtil fu(_manager_file);
            if (fu.Exists() == false)
            {
                std::cout << "manager_file in not exist" << std::endl;
                return false;
            }
            std::string str;
            fu.GetContent(&str); // 读取原数据管理文件内容

            Json::Value root;
            bucai::jsonUtil::UnSerialize(str, &root); // 反序列化
            for (int i = 0; i < root.size(); i++)
            {
                DataInfo info;
                info.pack_flag = root[i]["pack_flag"].asBool();
                info.fsize = root[i]["fsize"].asInt64();
                info.atime = root[i]["atime"].asInt64();
                info.mtime = root[i]["mtime"].asInt64();
                info.packpath = root[i]["pack_path"].asString();
                info.realpath = root[i]["real_path"].asString();
                info.url = root[i]["url"].asString();
                Insert(info.url, info); // 因为再文件操作中是覆盖写，所以不用害怕造成重复的内容。
            }
            return true;
        }
        bool Storage()
        { // 每次有信息改变则需要持久化存储⼀次
            std::vector<DataInfo> vec;
            GetAll(&vec);
            Json::Value root;

            for (auto e : vec)
            {
                Json::Value json;
                json["pack_flag"] = e.pack_flag;
                json["mtime"] = (Json::Int64)e.mtime;
                json["atime"] = (Json::Int64)e.atime;
                json["fsize"] = (Json::Int64)e.fsize;
                json["realpath"] = e.realpath;
                json["url"] = e.url;
                json["packpath"] = e.packpath;
                root.append(json);
            }

            std::string str;
            bucai::jsonUtil::Serialize(root, &str);

            bucai::fileUtil fu(_manager_file);
            fu.SetContent(str);
            return true;
        }
        bool Insert(const std::string &urlkey, const DataInfo &val)
        {
            pthread_rwlock_wrlock(&_rwlock); // 创建写锁
            _table[urlkey] = val;
            Storage(); // 因为再文件操作中是覆盖写，所以不用害怕造成重复的内容。
            pthread_rwlock_unlock(&_rwlock);
            return true;
        }
        bool Update(const std::string &key, const DataInfo &val)
        {
            pthread_rwlock_wrlock(&_rwlock); // 创建写锁
            _table[key] = val;
            Storage();
            pthread_rwlock_unlock(&_rwlock);
            return true;
        }
        bool GetOneByURL(const std::string &key, DataInfo *info)
        {
            if (_table.size() == 0)
            {
                std::cout << "_table is null" << std::endl;
                return false;
            }
            pthread_rwlock_wrlock(&_rwlock);
            auto it = _table.find(key);
            if (it == _table.end())
            {
                std::cout << "key is not exist" << std::endl;
                return false;
            }
            *info = it->second;
            pthread_rwlock_destroy(&_rwlock);
            return true;
        }
        bool GetOneByRealPath(const std::string &key, DataInfo *info)
        {
            if (_table.size() == 0)
            {
                std::cout << "_table is null" << std::endl;
                return false;
            }
            pthread_rwlock_wrlock(&_rwlock); // 创建写锁

            for (auto e : _table)
            {
                if (e.second.realpath == key)
                {
                    *info = e.second;
                    pthread_rwlock_destroy(&_rwlock);

                    return true;
                }
            }
            pthread_rwlock_destroy(&_rwlock);

            return false;
        }
        bool GetAll(std::vector<DataInfo> *arry)
        {
            if (_table.size() == 0)
            {
                std::cout << "_table is null" << std::endl;
                return false;
            }
            pthread_rwlock_wrlock(&_rwlock); // 创建写锁

            for (auto e : _table)
            {
                arry->push_back(e.second);
            }
            pthread_rwlock_destroy(&_rwlock);

            return true;
        }
    };
}
#endif

// void test1()
// {
//     DataInfo nn1;
//     nn1.NewDataInfo("./httplib.conf");
//     DataManager dtm;

//     dtm.Insert(nn1.url, nn1);

//     DataInfo bi;
//     dtm.GetOneByURL(nn1.url, &bi);

//     std::cout << bi.pack_flag << std::endl;
//     std::cout << bi.mtime << std::endl;
//     std::cout << bi.atime << std::endl;
//     std::cout << bi.fsize << std::endl;
//     std::cout << bi.realpath << std::endl;
//     std::cout << bi.url << std::endl;
//     std::cout << bi.packpath << std::endl;
// }

// void test2()
// {
//     DataInfo nn1;
//     nn1.NewDataInfo("./httplib.conf");

//     DataInfo bi;
//     DataManager dam;
//     dam.Insert(nn1.url, nn1);

//     dam.GetOneByRealPath("./httplib.conf", &bi);
//     std::cout << bi.pack_flag << std::endl;
//     std::cout << bi.mtime << std::endl;
//     std::cout << bi.atime << std::endl;
//     std::cout << bi.fsize << std::endl;
//     std::cout << bi.realpath << std::endl;
//     std::cout << bi.url << std::endl;
//     std::cout << bi.packpath << std::endl;
// }

// void test3()
// {
//     DataInfo nn1;
//     nn1.NewDataInfo("./httplib.conf");

//     DataInfo bi;
//     DataManager dam;
//     dam.Insert(nn1.url, nn1);

//     std::vector<DataInfo> vec;
//     dam.GetAll(&vec);

//     for (auto bi : vec)
//     {
//         std::cout << bi.pack_flag << std::endl;
//         std::cout << bi.mtime << std::endl;
//         std::cout << bi.atime << std::endl;
//         std::cout << bi.fsize << std::endl;
//         std::cout << bi.realpath << std::endl;
//         std::cout << bi.url << std::endl;
//         std::cout << bi.packpath << std::endl;
//         std::cout << "---------------------" << std::endl;
//     }
// }

// void test4()
// {
//     DataManager dm;
//     dm.InitLoad();

//     std::vector<DataInfo> vec;
//     dm.GetAll(&vec);

//     for (auto bi : vec)
//     {
//         std::cout << bi.pack_flag << std::endl;
//         std::cout << bi.mtime << std::endl;
//         std::cout << bi.atime << std::endl;
//         std::cout << bi.fsize << std::endl;
//         std::cout << bi.realpath << std::endl;
//         std::cout << bi.url << std::endl;
//         std::cout << bi.packpath << std::endl;
//         std::cout << "---------------------" << std::endl;
//     }
// }

// int main()
// {
//     test4();

//     return 0;
// }